<template>
  <div class="index-jvm-root">
    <div class="jvm-top">
      <el-button @click="refreshAll()" type="info">全局刷新</el-button>
      <el-button-group style="margin-left: 10px;">
        <el-button type="info" :icon="Download">线程堆栈</el-button>
        <el-button type="info" :icon="Download">对象统计</el-button>
        <el-button type="info" :icon="Download">死锁信息</el-button>
      </el-button-group>
    </div>

    <div class="jvm-info">
      <infoBasic class="info-basic" ref="InfoBasicRef" />
      <infoMachinePhysical class="info-machine-physical" ref="InfoMachinePhysicalRef"></infoMachinePhysical>
      <chartLineGc class="chart-line-gc info-root" ref="ChartLineGcRef"></chartLineGc>
    </div>

    <div class="chart-container">
      <div class="jvm-memory-bar info-root" ref="ChartBarMemoryRef" v-loading="chartData.old.use === 0">
      </div>

      <div class="chart-right-container">
        <chartLineMemory class="chart-line-memory info-root" ref="ChartLineMemoryRef"></chartLineMemory>
        <chartLineThread class="chart-line-thread info-root" ref="ChartLineThreadRef"></chartLineThread>
      </div>

    </div>
  </div>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router'
import { ref, onMounted, provide } from "vue";
import { Download } from '@element-plus/icons-vue';
import { useDark } from '@vueuse/core';
import { jvmMemoryApi, monitorAppsTreeApi, getMachineApi } from "@/api/guardcat";
import { isNotNull, isNull } from "@/assets/utils/obj";
import * as jvmTheme from './JvmTheme';
// component
import infoBasic from '../InfoBasic.vue';
import infoMachinePhysical from './InfoMachinePhysical.vue';
import chartLineMemory from './ChartLineMemory.vue';
import chartLineGc from './ChartLineGc.vue';
import chartLineThread from './ChartLineThread.vue';
// echarts
import * as echartTheme from '@/assets/styles/xz/echartTheme';
import * as echarts from 'echarts/core';
import { GridComponent, TooltipComponent, TitleComponent } from 'echarts/components';
import { BarChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([GridComponent, TooltipComponent, TitleComponent, BarChart, UniversalTransition, CanvasRenderer]);

// -----
const isDark = useDark();
const route = useRoute()

/** ======================================================================
 * 外部
 * ======================================================================*/
const currentMachine = ref<any>({})
provide('currentMachine', currentMachine);

const InfoBasicRef = ref();
const InfoMachinePhysicalRef = ref();
const ChartLineMemoryRef = ref();
const ChartLineGcRef = ref();
const ChartLineThreadRef = ref();

const refreshAll = () => {
  getJvmMemoryApi();
  InfoMachinePhysicalRef.value.reload();
  ChartLineMemoryRef.value.getJvmMemoryLine();
  ChartLineGcRef.value.getJvmGcLine();
  ChartLineThreadRef.value.getJvmThreadLine();
}
/** ======================================================================
 * JVM 内存模型
 * ======================================================================*/
const emphasisStyle = {
  itemStyle: {
    shadowBlur: 10,
    shadowColor: 'rgba(0,0,0,0.3)'
  }
};
const config = {
  rotate: 90,
  align: 'left',
  verticalAlign: 'middle',
  position: 'insideBottom',
  distance: 15,
  onChange: function () {
    const labelOption = {
      rotate: config.rotate,
      align: config.align,
      verticalAlign: config.verticalAlign,
      position: config.position,
      distance: config.distance
    };
    chartBarMemory.setOption({ series: [{ label: labelOption }, { label: labelOption }] });
  }
};
const labelOption = {
  show: true,
  position: config.position,
  distance: config.distance,
  align: config.align,
  verticalAlign: config.verticalAlign,
  rotate: config.rotate,
  formatter: (params: any) => {
    return params.name
  },
  fontSize: 16,
  color: isDark.value ? '#DADADA' : '#363636',
  fontFamily: echartTheme.common().fontFamily,
  rich: {
    name: {}
  }
};
const lebalOptionSimple = {
  show: true,
  color: labelOption.color,
  fontFamily: labelOption.fontFamily,
  formatter: labelOption.formatter
}
const defaultMemory = {
  heap: { max: 0, use: 0, field: 'hu', value: 1, percent: 0, name: '堆内存 Heap', itemStyle: { color: jvmTheme.clr().heap } },
  old: { max: 0, use: 0, field: 'ou', value: 0, percent: 0, name: '老年代\nOld', itemStyle: { color: jvmTheme.clr().old } },
  eden: { max: 0, use: 0, field: 'eu', value: 0, percent: 0, name: '新生代\nEden', itemStyle: { color: jvmTheme.clr().eden }, label: labelOption },
  survivor0: { max: 0, use: 0, field: 'su', value: 0, percent: 0, name: '幸存代', itemStyle: { color: jvmTheme.clr().survivor }, label: lebalOptionSimple },
  nonHeap: { max: 0, use: 0, field: 'nhu', value: 1, percent: 0, name: '非堆内存 NonHeap', itemStyle: { color: jvmTheme.clr().nonHeap } },
  metaspace: { max: 0, use: 0, field: 'mu', value: 0, percent: 0, name: '元空间 MetaSpace', itemStyle: { color: jvmTheme.clr().metaspace } },
  codeCache: { max: 0, use: 0, field: 'ccu', value: 0, percent: 0, name: 'JIT', itemStyle: { color: jvmTheme.clr().survivor }, label: lebalOptionSimple },
  ccs: { max: 0, use: 0, field: 'ccsu', value: 0, percent: 0, name: '类压缩', itemStyle: { color: jvmTheme.clr().survivor }, label: lebalOptionSimple },
}

const ChartBarMemoryRef = ref();
let chartBarMemory: any;
let chartData = defaultMemory;

const getJvmMemoryApi = () => {
  jvmMemoryApi({ machineId: currentMachine.value.machineId }).then((resp: any) => {
    if (isNotNull(resp.data)) {
      chartData.heap.max = Number(resp.data.heapUsage.committedValue);
      chartData.heap.use = Number(resp.data.heapUsage.usedValue);

      chartData.old.max = Number(resp.data.oldGen.maxValue);
      chartData.old.use = Number(resp.data.oldGen.usedValue);
      chartData.old.value = Number((chartData.old.use / chartData.heap.use).toFixed(6));
      chartData.old.percent = chartData.old.value;

      chartData.eden.max = Number(resp.data.edenSpace.committedValue);
      chartData.eden.use = Number(resp.data.edenSpace.usedValue);
      chartData.eden.value = Number((chartData.eden.use / chartData.heap.use).toFixed(6));
      if (chartData.eden.value < 0.1) {
        chartData.eden.percent = chartData.eden.value;
        let diff = 0.1 - chartData.eden.value;
        chartData.eden.value = chartData.eden.value + diff;
        chartData.old.value = chartData.old.value - diff;
      }

      chartData.survivor0.max = Number(resp.data.survivorSpace.maxValue);
      chartData.survivor0.use = Number(resp.data.survivorSpace.usedValue);
      chartData.survivor0.value = 1 - chartData.eden.value - chartData.old.value;
      if (chartData.survivor0.value < 0.03) {
        chartData.survivor0.percent = chartData.survivor0.value;
        let diff = 0.03 - chartData.survivor0.value;
        chartData.survivor0.value = chartData.survivor0.value + diff;
        chartData.old.value = chartData.old.value - diff;
      }

      chartData.nonHeap.max = Number(resp.data.nonHeapUsage.committedValue);
      chartData.nonHeap.use = Number(resp.data.nonHeapUsage.usedValue);

      chartData.metaspace.max = Number(resp.data.metaspaceUsage.committedValue);
      chartData.metaspace.use = Number(resp.data.metaspaceUsage.usedValue);
      chartData.metaspace.value = Number((chartData.metaspace.use / chartData.nonHeap.use).toFixed(6));

      chartData.codeCache.max = Number(resp.data.codeCacheUsage.committedValue);
      chartData.codeCache.use = Number(resp.data.codeCacheUsage.usedValue);
      chartData.codeCache.value = Number((chartData.codeCache.use / chartData.nonHeap.use).toFixed(6));

      chartData.ccs.max = Number(resp.data.compressedClassSpace.committedValue);
      chartData.ccs.use = Number(resp.data.compressedClassSpace.usedValue);
      chartData.ccs.value = 1 - chartData.metaspace.value - chartData.codeCache.value;
    } else {
      chartData = defaultMemory;
    }
    renderChart();
  })
}

const renderChart = () => {
  chartBarMemory.setOption({
    tooltip: {
      backgroundColor: echartTheme.tooltip().backgroundColor,
      borderColor: echartTheme.tooltip().borderColor,
      borderWidth: echartTheme.tooltip().borderWidth,
      textStyle: echartTheme.tooltip().textStyle,
      extraCssText: echartTheme.tooltip().extraCssText,
      formatter: (params: any) => {
        let name = '<span>' + params.data.name + '</span>';
        let max = '<span>最大：' + (params.data.max / 1024 / 1024).toFixed(2) + ' MB</span>';
        let use = '<span>使用：' + (params.data.use / 1024 / 1024).toFixed(2) + ' MB</span>';
        let value = '<span>百分比：' + (params.data.value * 100).toFixed(2) + '%</span>';
        if (params.data.percent > 0) {
          value = '<span>百分比：' + (params.data.percent * 100).toFixed(2) + '%</span>';
        }
        return (
          '<div style="text-align: left">' + name + '<br/>' + max + '<br/>' + use + '<br/>' + value + '</div>'
        );
      }
    },
    xAxis: { data: ['堆内存', '非堆内存'], axisLabel: { fontWeight: 700 }, axisLine: { onZero: true }, splitLine: { show: false }, splitArea: { show: false } },
    yAxis: { show: false },
    grid: { top: 20, bottom: 30, left: 10, right: 10 },
    series: [
      {
        type: 'bar',
        stack: 'one',
        label: labelOption,
        emphasis: emphasisStyle,
        data: [chartData.heap, chartData.nonHeap]
      },
      {
        type: 'bar',
        stack: 'two',
        label: labelOption,
        emphasis: emphasisStyle,
        data: [chartData.old, chartData.metaspace]
      },
      {
        type: 'bar',
        stack: 'two',
        emphasis: emphasisStyle,
        data: [chartData.eden, chartData.ccs]
      },
      {
        type: 'bar',
        stack: 'two',
        emphasis: emphasisStyle,
        data: [chartData.survivor0, chartData.codeCache]
      }
    ]
  });
}

const getRouteQueryParams = () => {
  let routeAppName = route.query.appName;
  let routeMachineId = route.query.machineId;
  if (isNull(routeMachineId)) {
    monitorAppsTreeApi().then((resp: any) => {
      currentMachine.value = resp.data[0].children[0];
      refreshAll();
    })
  } else {
    getMachineApi({ appName: routeAppName, machineId: routeMachineId }).then((resp: any) => {
      currentMachine.value = resp.data;
      refreshAll();
    })
  }
}
onMounted(() => {
  chartBarMemory = echarts.init(ChartBarMemoryRef.value);
  chartBarMemory.on('click', (params: any) => {
    ChartLineMemoryRef.value.getJvmMemoryLine(params.data.field);
  });
  chartBarMemory.resize();
  getRouteQueryParams();
})
</script>

<style scoped lang="scss">
.index-jvm-root {
  @include box(100%, 100%);

  .info-root {
    border: 0;
  }

  .jvm-top {
    @include box(100%, 25px);
    @include flex(row, flex-end, center);

    :deep(.el-icon) {
      @include box(18px, 20px);

      svg {
        @include box(18px, 20px)
      }
    }
  }

  .jvm-info {
    @include box(100%, 180px);
    @include flex(row, flex-start, center);
    min-height: 180px;
    margin-top: 10px;

    .info-basic {
      @include box(500px, 100%, 500px, 500px);
    }

    .info-machine-physical {
      @include box(550px, 100%);
      margin-left: 15px;
    }

    .chart-line-gc {
      @include box(calc(100% - 500px - 550px), 100%);
      margin-left: 15px;
    }
  }

  .chart-container {
    @include box(100%, calc(100% - 180px - 15px - 35px));
    @include flex(row, flex-start, center);
    margin-top: 15px;

    .jvm-memory-bar {
      @include box(500px, 100%, 500px, 500px);
    }

    .chart-right-container {
      @include box(calc(100% - 500px), 100%);
      margin-left: 15px;

      .chart-line-memory {
        @include box(100%, 300px);
      }

      .chart-line-thread {
        @include box(100%, calc(100% - 300px - 15px));
        margin-top: 15px;
      }
    }
  }

}
</style>